namespace Angelus.Oracle.Entity.Common.Entity
{
    using System;
    using System.Collections.Generic;
    using System.Data.Common.CommandTrees;
    using System.Data.Metadata.Edm;

    /// <summary>
    /// A Join symbol is a special kind of Symbol. It has to carry additional information
    /// <list type="bullet">
    /// <item>ColumnList for the list of columns in the select clause if this
    /// symbol represents a sql select statement. This is set by <see cref="SqlGenerator.AddDefaultColumns" />.</item>
    /// <item>ExtentList is the list of extents in the select clauses.</item>
    /// <item>FlattenedExtentList - if the Join has multiple extents flattented at the
    /// top level, we need this information to ensure that extent aliases are renamed
    /// correctly in <see cref="SqlSelectStatement.WriteSql"/>.</item>
    /// <item>NameToExtent has all the extents in ExtentList as a dictionary.
    /// This is used by <see cref="SqlGenerator.Visit(DbPropertyExpression)"/> to flattent
    /// record accesses.</item>
    /// <item>IsNestedJoin - is used to determine whether a JoinSymbol is an
    /// ordinary join symbol, or one that as a corresponding SqlSelectStatement.</item>
    /// </list>
    /// All the lists are set exactly once, and then used for lookups/enumerated.
    /// </summary>
    internal class JoinSymbol : Symbol
    {
        #region Fields

        private readonly List<Symbol> _extents;
        private readonly Dictionary<string, Symbol> _names;
        private List<Symbol> _columns;
        private List<Symbol> _fattenedExtents;

        #endregion

        #region Constructors

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="usage"></param>
        /// <param name="extents"></param>
        public JoinSymbol(string name, TypeUsage usage, List<Symbol> extents)
            : base(name, usage)
        {
            this._extents = new List<Symbol>(extents.Count);
            this._names = new Dictionary<string, Symbol>(extents.Count, StringComparer.OrdinalIgnoreCase);

            extents.ForEach(symbol =>
                                {
                                    this._names[symbol.Name] = symbol;
                                    this._extents.Add(symbol);
                                });
        }

        #endregion

        #region Properties

        /// <summary>
        /// 
        /// </summary>
        public List<Symbol> ColumnList
        {
            get
            {
                if (null == this._columns)
                {
                    this._columns = new List<Symbol>();
                }

                return this._columns;
            }
            set { this._columns = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        public List<Symbol> ExtentList
        {
            get { return this._extents; }
        }

        /// <summary>
        /// 
        /// </summary>
        public List<Symbol> FlattenedExtentList
        {
            get
            {
                if (null == this._fattenedExtents)
                {
                    this._fattenedExtents = new List<Symbol>();
                }

                return this._fattenedExtents;
            }
            set { this._fattenedExtents = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        public Dictionary<string, Symbol> NameToExtent
        {
            get { return this._names; }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsNestedJoin { get; set; }

        #endregion
    }
}